#ifdef RT_USE_OLD_CONN_WRAP

#include "RtBase.h"
#include "RtCnSendBuf.h"

#if !defined (_NEW_PROTO_TP)

CRtCnSendBuf::CRtCnSendBuf(DWORD dwMaxBufLen)
{
	m_dwMaxBufLen = dwMaxBufLen;
	m_pSendPDU = NULL;		//which SendPDU current m_pSend in
	m_dwSeqACKed = 0;		//sequence ACKed by peer
	m_dwSeq4ACK = 0;		//sequence for ACK to peer

	m_dwPDUStartSeq = 1;	//sequence begin with 1

	m_pmb = NULL;

	m_dwRoomsUsed = 0;

#ifdef PRINT_CONN_DETAIL_LOG
	m_sendNum = 0;
#endif
}

CRtCnSendBuf::~CRtCnSendBuf()
{
	if(m_listPDU.size() > 0)
	{
		RT_WARNING_TRACE_THIS("CRtCnSendBuf::~CRtCnSendBuf there have some pdu's ack not arrived! count = " << m_listPDU.size());
	}
	Reset();
}

void CRtCnSendBuf::Reset()
{
	//Empty the PDU list
	for(list<CRtCnPduSendItem*>::iterator it = m_listPDU.begin(); it != m_listPDU.end(); it++)
	{
		delete *it;
	}
	m_listPDU.clear();
	if(m_pmb)
	{
		m_pmb->DestroyChained();
		m_pmb = NULL;
	}

	m_pSendPDU = NULL;		
	m_dwSeqACKed = 0;		
	m_dwSeq4ACK = 0;		

	m_dwPDUStartSeq = 1;	

	m_dwRoomsUsed = 0;
}

DWORD CRtCnSendBuf::GetMaxBufLen()
{
	return m_dwMaxBufLen;
}

void CRtCnSendBuf::SetMaxBufLen(DWORD dwMaxBufLen)
{
//	RT_ASSERTE(dwMaxBufLen > 0);

	m_dwMaxBufLen = dwMaxBufLen;
	if(m_dwMaxBufLen == 0)
		Reset();
}

void CRtCnSendBuf::SetSeq4ACK(DWORD dwSeq4ACK)
{
	if(dwSeq4ACK == 0)
	{
		RT_INFO_TRACE_THIS("CRtCnSendBuf::SetSeq4ACK, m_dwSeq4ACK="<<m_dwSeq4ACK);
	}
	m_dwSeq4ACK = dwSeq4ACK;
		
}

void CRtCnSendBuf::AddToList(CRtCnPduSendItem *pSendPDU)
{
	RT_ASSERTE_RETURN_VOID(pSendPDU);
	if(!m_listPDU.empty())//means this SendPDU is the first member of the list
	{
		m_listPDU.back()->SetNext(pSendPDU);//let the last SendPDU in the list point to me
	}
	else
	{	
		RT_ASSERTE(!m_pSendPDU);
	}
	if(!m_pSendPDU)
	{
//		RT_INFO_TRACE_THIS("CRtCnSendBuf::AddToList, list size="<<m_listPDU.size());
		m_pSendPDU = pSendPDU;
		PrepareSendMB();
	}

	//push me in
	m_listPDU.push_back(pSendPDU);
	m_dwRoomsUsed += (pSendPDU->GetFixLength() + pSendPDU->GetDataLen());
}

DWORD CRtCnSendBuf::AddKeepAlivePDU()
{
	if(m_pSendPDU)
		return 0;
	BYTE byType = CS_PDU_TYPE_KEEPALIVE;

/*
	if( m_dwMaxBufLen < m_dwRoomsUsed 
		|| (m_dwMaxBufLen - m_dwRoomsUsed) < CRtCnPduBase::GetFixLength(byType)) //no space available
	{
		RT_WARNING_TRACE_THIS("CRtCnSendBuf::AddKeepAlivePDU(), SendBuf is full, Keep Alive cannot be add.");
		return 0;//no space available
	}
*/

	//Create a new Keep Alive PDU
	CRtCnPduKeepAlive *pSendPDU = new CRtCnPduKeepAlive(m_dwSeq4ACK, byType);

	////Add this PDU to list
	AddToList(pSendPDU);

	return pSendPDU->GetFixLength();
}

DWORD CRtCnSendBuf::AddDisconnPDU(RtResult aReason)
{
	//Create a new Keep Alive PDU
	CRtCnPduDisconn *pSendPDU = new CRtCnPduDisconn(aReason, CS_PDU_TYPE_DISCONN);

	////Add this PDU to list
	AddToList(pSendPDU);

	return pSendPDU->GetFixLength();
}

DWORD CRtCnSendBuf::AddDataPDU(CRtMessageBlock *pData, BOOL bPDUNeedACK)
{
	BYTE byType = CS_PDU_TYPE_DATA_NORMAL;
	DWORD dwDataLen = pData->GetChainedLength();
	
//	RT_INFO_TRACE_THIS("CRtCnSendBuf::AddDataPDU(), dwDataLen = " << dwDataLen);
	
	DWORD dwWholePDULen = dwDataLen + CRtCnPduBase::GetFixLength(byType, bPDUNeedACK);
	
	if(m_dwMaxBufLen < m_dwRoomsUsed 
		/*|| (m_dwMaxBufLen - m_dwRoomsUsed) < dwWholePDULen*/) //no space available
	{
#ifdef PRINT_CONN_DETAIL_LOG
		RT_INFO_TRACE_THIS("CRtCnSendBuf::AddDataPDU(), no space available roomUsed="<<m_dwRoomsUsed);
#endif
		return 0;
	}
	
	//how many seq# can use before reach the limit
	if(SEQUENCE_LIMIT - m_dwPDUStartSeq < dwWholePDULen)
	{
		RT_INFO_TRACE_THIS("CRtCnSendBuf::AddDataPDU, reset startSeq. StartSeq="<<m_dwPDUStartSeq<<" len="<<dwWholePDULen);
		m_dwPDUStartSeq = 1;	//reset seq# to 1
		byType = CS_PDU_TYPE_DATA_START;
	}

	CRtMessageBlock *pDataTmp = pData->DuplicateChained();
	pData->AdvanceChainedReadPtr(dwDataLen);//Advance upper layer data read ptr
	//Create a new Data PDU
	CRtCnPduData *pSendPDU = new CRtCnPduData(bPDUNeedACK, m_dwSeq4ACK, dwDataLen, byType, pDataTmp);

	pSendPDU->SetSeqStart(m_dwPDUStartSeq);

	////Add this PDU to list
	AddToList(pSendPDU);

	//prepare for NEXT SendPDU
	m_dwPDUStartSeq = pSendPDU->GetSeqEnd() + 1;//the Seq next SendPDU should begin with
#ifdef PRINT_CONN_DETAIL_LOG
	RT_INFO_TRACE_THIS("CRtCnSendBuf::AddDataPDU, endSeq="<<pSendPDU->GetSeqEnd()<<" sendNum="<<++m_sendNum<<" acked="<<m_dwSeqACKed);
#endif
	return dwDataLen;
}

BYTE CRtCnSendBuf::GetData(CRtMessageBlock* &pmb, DWORD& dwMsgACK)
{
//	RT_INFO_TRACE_THIS("CRtCnSendBuf::GetData, m_pmb="<<m_pmb<<", m_pSendPDU="<<m_pSendPDU);
	pmb = m_pmb;
	dwMsgACK = m_dwOldSeq4ACK;
	return m_pSendPDU?m_pSendPDU->GetType():CS_PDU_TYPE_NONE;

}

void CRtCnSendBuf::DataSentLen(DWORD dwDataLen)
{

	RT_ASSERTE(m_pmb);
	RT_ASSERTE(m_pmb->GetChainedLength() == 0);
	m_pmb->DestroyChained();
	m_pmb = NULL;
	RT_ASSERTE_RETURN_VOID(m_pSendPDU);
	m_pSendPDU = m_pSendPDU->GetNext();
	PrepareSendMB();
//	RT_INFO_TRACE_THIS("CRtCnSendBuf::DataSentLen, m_pmb="<<m_pmb<<", m_pSendPDU="<<m_pSendPDU);
	
}

void CRtCnSendBuf::ClearAllSent()
{
	CRtCnPduSendItem* pSendPdu;
	list<CRtCnPduSendItem*>::iterator it,it_begin;
	it = it_begin = m_listPDU.begin();
	for(; it != m_listPDU.end(); it++)
	{
		pSendPdu = *it;
		if(m_pSendPDU && pSendPdu == m_pSendPDU)
			break;
		delete pSendPdu;
	}
	if(it != it_begin)
		m_listPDU.erase(it_begin, it);
}

int CRtCnSendBuf::DoACK(DWORD dwSeqACKed)
{
#ifdef PRINT_CONN_DETAIL_LOG
	RT_INFO_TRACE_THIS("CRtCnSendBuf::DoACK, ack=" << dwSeqACKed << " m_dwSeqACKed=" << m_dwSeqACKed);
#endif

	if(m_listPDU.empty() )//Already ACKed, when Reconnecting could bring this condition
		return -2;//Nothing to do

	CRtCnPduSendItem *pSendPDU = NULL;
	list<CRtCnPduSendItem*>::iterator it_begin = m_listPDU.begin();
	list<CRtCnPduSendItem*>::iterator it = it_begin;

	//Flag indicate if Reconnection ACK successfully
	////If previous DropDataIfCan() successful <m_dwSeqACKed haven't Advance, but Actually PDU dropped>, 
	////this MAY cause reconnection failed.
	////When reconnect ACK, PDUs SHOULD be moved had been moved by DropDataIfCan() before,
	////SendBuf Ptr cannot backward to the wish point, data got lost, reconnection failed!
	int nReconnACK = 0;

	//ACK has been rewound, we should DoACK to PDU whose start Seq# before 1
	//ACK all PDUs before Seq# rewind
	if(dwSeqACKed < m_dwSeqACKed)
	{
		RT_INFO_TRACE_THIS("CRtCnSendBuf::DoACK(), Seq# may rewind, dwSeqACKed = " << dwSeqACKed << ", m_dwSeqACKed = " << m_dwSeqACKed<<",m_dwPDUStartSeq="<<m_dwPDUStartSeq);
		for(;it != m_listPDU.end(); it++)
		{
			pSendPDU = *it;
			if(pSendPDU->GetType() == CS_PDU_TYPE_DATA_START && pSendPDU->GetSeqStart() == 1)
				break;
			m_dwRoomsUsed -= (pSendPDU->GetFixLength() + pSendPDU->GetDataLen());
			RT_ASSERTE(pSendPDU != m_pSendPDU);
			if(pSendPDU == m_pSendPDU)
				break;
			delete pSendPDU;
		}
	}

	//All below do when (dwSeqACKed > m_dwSeqACKed)
	//find which SendPDU dwSeqACKed belong to
	CRtCnPduSendItem* pNext;
	for(;it != m_listPDU.end(); it++)
	{
		pSendPDU = *it;
		if(!pSendPDU)
		{
			continue;
		}
		if(dwSeqACKed < pSendPDU->GetSeqEnd())
		{
			if((dwSeqACKed + 1) == pSendPDU->GetSeqStart())
				nReconnACK = 1;//But if it happen to the point!
			else
				RT_ERROR_TRACE_THIS("CRtCnSendBuf::DoACK, not match! dwSeqACKed="<<dwSeqACKed
					<<" m_dwSeqACKed="<<m_dwSeqACKed
					<<" start="<<pSendPDU->GetSeqStart()
					<<" end="<<pSendPDU->GetSeqEnd()
					<<" type="<<pSendPDU->GetType()
					<<" m_dwPDUStartSeq="<<m_dwPDUStartSeq);
			break;
		}

		if(pSendPDU == m_pSendPDU)
		{
			if (pSendPDU->GetType()!=CS_PDU_TYPE_KEEPALIVE)
			{
				RT_ERROR_TRACE_THIS("CRtCnSendBuf::DoACK handle the sending PDU, dwSeqACKed="<<dwSeqACKed
					<<" m_dwSeqACKed="<<m_dwSeqACKed
					<<" m_dwPDUStartSeq="<<m_dwPDUStartSeq
					<<" start="<<pSendPDU->GetSeqStart()
					<<" end="<<pSendPDU->GetSeqEnd());
			}
			nReconnACK = 1;
			break;
		}
		
		m_dwRoomsUsed -= (pSendPDU->GetFixLength() + pSendPDU->GetDataLen());
		if(pSendPDU->GetType() == CS_PDU_TYPE_DATA_START 
			|| pSendPDU->GetType() == CS_PDU_TYPE_DATA_NORMAL)
		{
			m_dwSeqACKed = pSendPDU->GetSeqEnd();
		}
		
		pNext = pSendPDU->GetNext();
		delete pSendPDU;
		if(pNext && pNext->GetSeqStart() == 1)
		{
			RT_INFO_TRACE_THIS("CRtCnSendBuf::DoACK, come back");
			++it;
			break;
		}
	}

	if(it != it_begin)
	{
		m_listPDU.erase(it_begin, it);
		nReconnACK = 1;
	}
	if(nReconnACK == 0)//No PDU dropped & dwSeqACKed != m_dwSeqACKed
	{
		return -1;
	}

	return 0;//OK
}

int CRtCnSendBuf::DoReconnACK(DWORD dwSeqACKed)
{
	RT_INFO_TRACE_THIS("CRtCnSendBuf::DoReconnACK, dwSeqACKed="<<dwSeqACKed<<" m_dwSeqACKed="<<m_dwSeqACKed<<" m_dwPDUStartSeq="<<m_dwPDUStartSeq);
	if (dwSeqACKed>=m_dwPDUStartSeq)
	{
		RT_ERROR_TRACE_THIS("CRtCnSendBuf::DoReconnACK, invalid ack seq!");
	}
	//Do ACK as usual
	if(DoACK(dwSeqACKed) == -1)//ACK for Reconn failed, SendBuf Ptr cannot backward to the wish point
	{
		RT_ERROR_TRACE_THIS("CRtCnSendBuf::DoReconnACK ack failed and return code is -1!");
		return -1;
	}
	if(m_pmb != NULL)
	{
		m_pmb->DestroyChained();
		m_pmb = NULL;
	}
	if(!m_listPDU.empty())
	{
		m_pSendPDU = m_listPDU.front();
		PrepareSendMB();
	}
	else
		m_pSendPDU = NULL;

	//As ACK do ack to a whole PDU
	//Reset the PDU sending pointer to the first PDU in the list
	return 0;//OK
}

void CRtCnSendBuf::PrepareSendMB()
{
	RT_ASSERTE(!m_pmb);
	//RT_ASSERTE(m_pSendPDU);
	if(!m_pSendPDU)
		return;

	m_pSendPDU->SetACK(m_dwSeq4ACK);
	CRtMessageBlock mb(m_pSendPDU->GetFixLength());
	m_pSendPDU->Encode(mb);
	m_pmb = mb.DuplicateChained();
	m_dwOldSeq4ACK = m_dwSeq4ACK;
}

#endif

#endif // RT_USE_OLD_CONN_WRAP

